﻿namespace Mp3Observer.Common.Behaviors
{
    #region usings

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    #endregion

    /// <summary>
    /// This class offers attached properties to the Treeview:
    /// ExpandedCommand
    /// SelectedItem
    /// HandleSelectionChanges must be set to true to get SelectedItem
    /// </summary>
    /// <example>
    /// <code>
    /// <![CDATA[ 
    /// <TreeView ItemsSource="{Binding DataSource}" 
    ///           behaviours:TreeViewSelectionBehaviours.HandleSelectionChanges="True" 
    ///           behaviours:TreeViewSelectionBehaviours.SelectedItem="{Binding SelectedItem, Mode=OneWayToSource}" 
    ///           behaviours:TreeViewSelectionBehaviours.ExpandedCommand="{Binding GetChildren}">...
    /// ...
    /// this.getChildren = new RelayCommand<object>(GetChildrenForItem)
    /// ...
    /// void GetChildrenForItem(object itemExpanded) { ...
    /// ]]>
    /// </code>
    /// </example>
    public sealed class TreeViewSelectionBehaviours
    {
        /// <summary>
        /// SelectedItem Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.RegisterAttached("SelectedItem",
            typeof(object),
            typeof(TreeViewSelectionBehaviours),
            new FrameworkPropertyMetadata(null));

        /// <summary>
        /// HandleSelectionChanges Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty HandleSelectionChangesProperty = DependencyProperty.RegisterAttached("HandleSelectionChanges",
            typeof(bool),
            typeof(TreeViewSelectionBehaviours),
            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnHandleSelectionChangesChanged)));

        /// <summary>
        /// ExpandedCommand Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty ExpandedCommandProperty = DependencyProperty.RegisterAttached("ExpandedCommand",
            typeof(ICommand),
            typeof(TreeViewSelectionBehaviours),
            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnExpandedCommandChanged)));

        /// <summary>
        /// Prevents a default instance of the <see cref="TreeViewSelectionBehaviours"/> class from being created.
        /// </summary>
        private TreeViewSelectionBehaviours()
        {
        }

        /// <summary>
        /// Gets the SelectedItem property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <returns>the selecteditem</returns>
        public static object GetSelectedItem(DependencyObject dependencyObject)
        {
            return (object)dependencyObject.GetValue(SelectedItemProperty);
        }

        /// <summary>
        /// Sets the SelectedItem property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="value">The value.</param>
        public static void SetSelectedItem(DependencyObject dependencyObject, object value)
        {
            dependencyObject.SetValue(SelectedItemProperty, value);
        }

        /// <summary>
        /// Gets the HandleSelectionChanges property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <returns>true or false</returns>
        public static bool GetHandleSelectionChanges(DependencyObject dependencyObject)
        {
            return (bool)dependencyObject.GetValue(HandleSelectionChangesProperty);
        }

        /// <summary>
        /// Sets the HandleSelectionChanges property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="value">if set to <c>true</c> [value].</param>
        public static void SetHandleSelectionChanges(DependencyObject dependencyObject, bool value)
        {
            dependencyObject.SetValue(HandleSelectionChangesProperty, value);
        }

        /// <summary>
        /// Gets the ExpandedCommand property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <returns>the ICommand</returns>
        public static ICommand GetExpandedCommand(DependencyObject dependencyObject)
        {
            return (ICommand)dependencyObject.GetValue(ExpandedCommandProperty);
        }

        /// <summary>
        /// Sets the ExpandedCommand property.  This dependency property
        /// indicates ....
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="value">The value.</param>
        public static void SetExpandedCommand(DependencyObject dependencyObject, ICommand value)
        {
            dependencyObject.SetValue(ExpandedCommandProperty, value);
        }

        /// <summary>
        /// Handles changes to the HandleSelectionChanges property.
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnHandleSelectionChangesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var source = (TreeView)dependencyObject;
            if ((bool)e.NewValue)
                source.SelectedItemChanged += SelectedItemChanged;
            else
                source.SelectedItemChanged -= SelectedItemChanged;
        }

        /// <summary>
        /// Sets the selectedItem
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The RoutedPropertyChangedEventArgs instance containing the event data.</param>
        private static void SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            SetSelectedItem((DependencyObject)sender, e.NewValue);
        }

        /// <summary>
        /// Handles changes to the ExpandedCommand property.
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnExpandedCommandChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var source = (TreeView)dependencyObject;
            // unregister in case this is not the first time the property is set
            source.RemoveHandler(TreeViewItem.ExpandedEvent, (RoutedEventHandler)ItemExpanded);
            source.AddHandler(TreeViewItem.ExpandedEvent, (RoutedEventHandler)ItemExpanded);
        }

        /// <summary>
        /// Expands the Item
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private static void ItemExpanded(object sender, RoutedEventArgs e)
        {
            var source = (TreeView)sender;
            var command = GetExpandedCommand(source);
            var item = (TreeViewItem)e.OriginalSource;
            if (command != null && command.CanExecute(item.Header))
                command.Execute(item.Header);
        }
    }
}
